import { createBot, Bot, BotOptions } from "mineflayer";
import { program } from "commander";
import dotenv from "dotenv";
import type { Block } from "prismarine-block";

program
    .argument("<username>")
    .argument("[password]")
    .option("-e, --env <file>", "specify .env file")
    .action(main)
    .parse(process.argv);

function milliseconds(count: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, count));
}

async function trySleep(bot: Bot, restart: () => void) {
    if (bot.isSleeping) return;

    const bed = bot.findBlock({
        matching: bot.isABed as (block: Block) => boolean,
    });

    if (bed) {
        bot.chat("Going to sleep!");
        try {
            await bot.sleep(bed);
            // If succeeds, return
            return;
        } catch (e) {
            bot.chat(`I can't sleep because ${(e as Error).message}`);
        }
    }

    bot.chat("Can't find a bed. I'll quit for 10 seconds. Good night!");
    await milliseconds(1000);
    bot.quit();
    await milliseconds(10000);
    restart();
}

function start(options: BotOptions) {
    const bot = createBot(options);

    bot.once("spawn", () => {
        bot.on("playerJoined", player => {
            bot.chat(`Hello, ${player.username}! Say z to make me sleep.`);
        });
        bot.on("chat", (username, message) => {
            if (username === bot.username) return;
            if (message && Array.from(message).every(c => c === "z")) {
                trySleep(bot, () => start(options));
            }
        });
    });

    bot.on("kicked", console.log);
    bot.on("error", console.log);
}

function main(username: string, password: string | undefined, options: { env?: string }) {
    dotenv.config({ path: options.env });

    start({
        host: process.env.HOST,
        port: process.env.PORT === undefined ? undefined : parseInt(process.env.PORT),
        username,
        password,
        version: process.env.VERSION,
    });
}
